﻿using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Text;
using NUnit.Framework;

namespace PropertyExpression.DynamicQueryStaticChecking.Test
{
    [TestFixture]
    public class MainTest
    {
        [Test]
        public void Test1()
        {
            Main();
        }

        public static void Main()
        {
            ExecutePartLinkQuery(
                dbCommand =>
                new PartLinkQueryProvider(
                    new PartLinkParams(
                        new Guid("..."),
                        new Guid("..."),
                        new Guid("...")
                        ).AsQueryParams(dbCommand),
                    new WithoutInnerPartLinkSwitcher()
                    )
                );

            ExecutePartLinkQuery(
                dbCommand =>
                new PartLinkQueryProvider(
                    new PartLinkParams(
                        new Guid("..."),
                        new Guid("..."),
                        new Guid("...")
                        ).AsQueryParams(dbCommand),
                    new WithInnerPartLinkSwitcher(
                        new InnerPartLinkParams(
                            new Guid("..."),
                            "en"
                            ).AsQueryParams(dbCommand),
                        false
                        )
                    )
                );

            ExecutePartLinkQuery(
                dbCommand =>
                new PartLinkQueryProvider(
                    new PartLinkParams(
                        new Guid("..."),
                        new Guid("..."),
                        new Guid("...")
                        ).AsQueryParams(dbCommand),
                    new WithInnerPartLinkSwitcher(
                        new InnerPartLinkParams(
                            new Guid("..."),
                            "en"
                            ).AsQueryParams(dbCommand),
                        true
                        )
                    )
                );
        }

        private static void ExecutePartLinkQuery(
            Func<DbCommand, IQueryProvider<PartLinkInfo>> queryProviderFunc)
        {
            Execute(
                transaction =>
                    {
                        using (var dbCommand = new SqlCommand())
                        {
                            dbCommand.Connection = transaction.Connection;
                            dbCommand.Transaction = transaction;

                            var queryProvider = queryProviderFunc(dbCommand);

                            queryProvider.FillDbCommand(dbCommand);

                            using (var reader = dbCommand.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    PartLinkInfo partLinkInfo = queryProvider.Materialize(reader);
                                    Console.WriteLine(partLinkInfo.PartNumber);
                                }
                            }
                        }
                    }
                );
        }

        private static void FillQuery(
            StringBuilder builder, IInnerPartLinkSwitcher innerPartLinkSwitcher, IParamManager<PartLinkParams> paramManager)
        {
        }

        private static TResult Execute<TResult>(Func<SqlTransaction, TResult> func)
        {
            TResult result;
            using (var connection = new SqlConnection(@"Data Source=dell32\MSSQLSERVER2008;Initial Catalog=Hitachi;Integrated Security=True"))
            {
                connection.Open();
                var transaction = connection.BeginTransaction(IsolationLevel.Serializable);
                try
                {
                    result = func(transaction);

                    transaction.Commit();
                }
                catch (Exception exception)
                {
                    try
                    {
                        transaction.Rollback();
                    }
                    catch
                    {
                        throw new ApplicationException("The transaction can not be rolled back.", exception);
                    }
                    throw;
                }
            }
            return result;
        }

        private static void Execute(Action<SqlTransaction> action)
        {
            Execute(transaction =>
                        {
                            action(transaction);
                            return new {};
                        }
                );
        }
    }

    public partial class PreTextTemplate1
    {
        private readonly IInnerPartLinkSwitcher innerPartLinkSwitcher;
        private readonly IParamManager<PartLinkParams> param;

        public StringBuilder Builder
        {
            get { return GenerationEnvironment; }
        }

        public PreTextTemplate1(IInnerPartLinkSwitcher innerPartLinkSwitcher, IParamManager<PartLinkParams> param)
        {
            this.innerPartLinkSwitcher = innerPartLinkSwitcher;
            this.param = param;
        }
    }

    public partial class PreTextTemplate2
    {
        private readonly IInnerPartLinkSwitcher innerPartLinkSwitcher;
        private readonly IParamManager<PartLinkParams> param;

        public StringBuilder Builder
        {
            get { return GenerationEnvironment; }
        }

        public PreTextTemplate2(IInnerPartLinkSwitcher innerPartLinkSwitcher, IParamManager<PartLinkParams> param)
        {
            this.innerPartLinkSwitcher = innerPartLinkSwitcher;
            this.param = param;
        }
    }

    public static class PartLinkParamsPropertyExpressionExtension
    {
        public static void FillDbCommand<T>(this IQueryProvider<T> it, DbCommand dbCommand)
        {
            throw new NotImplementedException();
        }

        public static T Materialize<T>(this IQueryProvider<T> it, DbDataReader dbDataReader)
        {
            throw new NotImplementedException();
        }

        public static string ServicePartsPageID(this IParamManager<PartLinkParams> it)
        {
            return it.Param(_ => _.ServicePartsPageID);
        }
        public static string ModelPublicationID(this IParamManager<PartLinkParams> it)
        {
            return it.Param(_ => _.ModelPublicationID);
        }
    }

    public static class InnerPartLinkParamsPropertyExpressionExtension
    {
        public static string LanguageCode(this IParamManager<InnerPartLinkParams> it)
        {
            return it.Param(_ => _.LanguageCode);
        }
        public static string ModelID(this IParamManager<InnerPartLinkParams> it)
        {
            return it.Param(_ => _.ModelID);
        }
    }

    public interface ILatestPublicationSwitcher
    {
        void AddSubQuery(Action action);
    }

    public class LatestPublicationSwitcher : ILatestPublicationSwitcher
    {
        public void AddSubQuery(Action action)
        {
            action();
        }
    }

    public class NoLatestPublicationSwitcher : ILatestPublicationSwitcher
    {
        public void AddSubQuery(Action action)
        {
            //no-op
        }
    }
}